#include "shared.h"

SharedHeader* SharedData::resize(int newSize)
{
  if (hFile == INVALID_HANDLE_VALUE || fileSize >= newSize) return (SharedHeader*) pShared;
  if (pShared) UnmapViewOfFile(pShared);
  if (hMapFile) CloseHandle(hMapFile);
  while (fileSize < newSize)
    fileSize += 1048576;
  SetFilePointer(hFile, fileSize, NULL, FILE_BEGIN);
  SetEndOfFile(hFile);
  hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, fileSize, NULL);
  pShared = (unsigned char*) (hMapFile ? MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, fileSize) : NULL);
  if (pShared)
    ((SharedHeader*) pShared)->size = fileSize;
  return (SharedHeader*) pShared;
}

SharedData::SharedData(bool master)
  : hFile(INVALID_HANDLE_VALUE)
  , hMapFile(NULL)
  , fileSize(0)
  , hMutex(NULL)
{
  TCHAR pBuf[MAX_PATH];
  GetTempPath(MAX_PATH, pBuf);
  wcscat(pBuf, L"KappaUISharedFile");
  hFile = CreateFile(pBuf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
  if (master)
  {
    SetFilePointer(hFile, 1048576, NULL, FILE_BEGIN);
    SetEndOfFile(hFile);
  }
  if (hFile != INVALID_HANDLE_VALUE)
  {
    fileSize = GetFileSize(hFile, NULL);
    hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, fileSize, NULL);
  }
  if (hMapFile)
    pShared = (unsigned char*) MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, fileSize);
  if (pShared)
  {
    if (master)
    {
      SharedHeader* hdr = (SharedHeader*) pShared;
      memset(hdr, 0, sizeof(SharedHeader));
      hdr->size = fileSize;
      hdr->end = sizeof(SharedHeader);
      hdr->mutexid = 0;
      do
      {
        if (hMutex) CloseHandle(hMutex);
        hdr->mutexid++;
        wsprintf(pBuf, L"Global\\KappaUIFileMutex%d", hdr->mutexid);
        hMutex = CreateMutex(NULL, FALSE, pBuf);
      } while (GetLastError() == ERROR_ALREADY_EXISTS);
    }
    else
    {
      wsprintf(pBuf, L"Global\\KappaUIFileMutex%d", header()->mutexid);
      hMutex = CreateMutex(NULL, FALSE, pBuf);
    }
  }
}
SharedData::~SharedData()
{
  if (hMutex)
    CloseHandle(hMutex);
  if (pShared)
    UnmapViewOfFile(pShared);
  if (hMapFile)
    CloseHandle(hMapFile);
  if (hFile)
    CloseHandle(hFile);
}

SharedHeader* SharedData::lock(DWORD timeout) const
{
  if (!hMutex || !pShared) return NULL;
  DWORD result = WaitForSingleObject(hMutex, timeout);
  if (result != WAIT_OBJECT_0 && result != WAIT_ABANDONED)
    return NULL;
  SharedHeader* hdr = (SharedHeader*) pShared;
  if (hdr->size > fileSize)
  {
    fileSize = hdr->size;
    UnmapViewOfFile(pShared);
    CloseHandle(hMapFile);
    hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, fileSize, NULL);
    pShared = (unsigned char*) (hMapFile ? MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, fileSize) : NULL);
  }
  return (SharedHeader*) pShared;
}
void SharedData::unlock() const
{
  ReleaseMutex(hMutex);
}

SharedHeader* SharedData::reset(int width, int height, int lines)
{
  SharedHeader* hdr = (SharedHeader*) pShared;

  if (lines < 32) lines = 32;
  hdr->maxLines = 1;
  while (hdr->maxLines < lines) hdr->maxLines *= 2;
  hdr->numLines = 0;

  hdr->end = sizeof(SharedHeader) + sizeof(FrameHeader) + sizeof(ScrollHeader) + sizeof(ReplyHeader) +
    sizeof(LineHeader) * hdr->maxLines + 3 * sizeof(unsigned long) * width * height;
  hdr = resize(hdr->end);

  unsigned long cur = sizeof(SharedHeader);
  hdr->offsFrame = cur; cur += sizeof(FrameHeader);
  hdr->offsScroll = cur; cur += sizeof(ScrollHeader);
  hdr->offsReply = cur; cur += sizeof(ReplyHeader);
  FrameHeader* frm = frame();
  ScrollHeader* scr = scroll();
  ReplyHeader* rep = reply();
  frm->offset = cur; cur += sizeof(unsigned long) * width * height;
  scr->offset = cur; cur += sizeof(unsigned long) * width * height;
  rep->offset = cur; cur += sizeof(unsigned long) * width * height;
  hdr->offsLines = cur;

  frm->updated = 1;
  frm->width = width;
  frm->height = height;
  frm->insetTop = 0;
  frm->insetBottom = 0;
  scr->updated = 1;
  rep->updated = 1;

  hdr->maxScroll = -height;

  return hdr;
}
LineHeader* SharedData::add(int width, int height)
{
  SharedHeader* hdr = (SharedHeader*) pShared;
  if (hdr->numLines >= hdr->maxLines)
  {
    unsigned long delta = sizeof(LineHeader) * hdr->maxLines;
    unsigned long start = hdr->offsLines + delta;
    hdr = resize(hdr->end + delta);
    memmove(pShared + start + delta, pShared + start, hdr->end - start);
    hdr->end += delta;
    hdr->maxLines *= 2;
    for (int i = 0; i < hdr->numLines; i++)
      ((LineHeader*) (pShared + hdr->offsLines + sizeof(LineHeader) * i))->offset += delta;
  }
  unsigned long size = sizeof(unsigned long) * width * height;
  hdr = resize(hdr->end + size);
  LineHeader* ptr = line(hdr->numLines++);
  ptr->updated = 1;
  ptr->width = width;
  ptr->height = height;
  ptr->offset = hdr->end;
  hdr->end += size;
  return ptr;
}
void SharedData::popline(int pos)
{
  SharedHeader* hdr = (SharedHeader*) pShared;
  LineHeader* lines = (LineHeader*) (pShared + ((SharedHeader*) pShared)->offsLines);
  int delta = lines[pos].height + lines[pos].insetTop + lines[pos].insetBottom;
  hdr->maxScroll -= delta;
  hdr->scrollPos -= delta;
  if (hdr->scrollPos < 0) hdr->scrollPos = 0;
  if (hdr->scrollPos > hdr->maxScroll) hdr->scrollPos = hdr->maxScroll;
  hdr->numLines--;
  for (int i = pos; i < hdr->numLines; i++)
  {
    lines[i] = lines[i + 1];
    if (lines[i].updated <= 0)
      lines[i].updated--;
  }
}
